//
// Created by shen on 2024/12/20.
//

#include "FAvFrame.h"
void* t_decode_avpacke(void *args){
    FAvFrame *fAvFrameHelper = static_cast<FAvFrame*>(args);
    fAvFrameHelper->decodeAVPackate();
    return 0;
}
int avformat_ff_callback(void *ctx){
    FAvFrame *fFmpeg = (FAvFrame *) ctx;
    if(fFmpeg->ffStatus->exit)
    {
        return AVERROR_EOF;
    }
    return 0;
}
int getAvPacketRefType2(AVPacket *pAvPkt){
    unsigned char *pData = pAvPkt->data; /* 帧数据 */
    unsigned char *pEnd = NULL;
    int dataSize=pAvPkt->size;
    int curSize=0;
    int naluSize=0;
    int i;
    unsigned char nalHeader,nalType,refType;

    while(curSize<dataSize){
        if (pEnd-pData<4){
            goto fail;
        }

        for (int i = 0; i < 4; ++i) {
            naluSize<<=8;
            naluSize|=pData[i];
        }

        pData+=4;

        if (naluSize>pEnd-pData+1||naluSize<=0){
            goto fail;
        }

        nalHeader = *pData;

        nalType = nalHeader&0x1F;

        refType = (nalHeader >> 5 )&0x03;

        if (nalType==5){
            return 1;
        }else if (nalType == 1){
            return refType;
        } else{
            return 1;
        }
    }
    fail:
    return 0;


}
void FAvFrame::decodeAvPacket() {
    int ret;
    LOGE("开始解码抽帧");
    while (ffStatus != NULL && !ffStatus->exit){
        if (isPause){
            av_usleep(1000*10);
            continue;
        }
        AVPacket *avPacket = av_packet_alloc();
        ret = av_read_frame(avFormatContext,avPacket);
        if (ret != 0){
            LOGE("获取视频avPacket 失败");
            av_usleep(1000*10);
            continue;
        }

        if (avPacket->stream_index != avStreamIndex){
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
        }else if (avPacket->stream_index == avStreamIndex){
            if (getAvPacketRefType2(avPacket) > 0){
                uint8_t *data;
                av_bitstream_filter_filter(mimType, avFormatContext->streams[avStreamIndex]->codec,
                                           NULL, &data, &avPacket->size, avPacket->data, avPacket->size, 0);
                uint8_t *tdata = NULL;
                tdata = avPacket->data;
                if(tdata != NULL)
                {
                    av_free(tdata);
                }

            }else{
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
            }
        }else{
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
        }

    }
    isExit = true;
    LOGE("抽帧结束");



}
int
FAvFrame::getAvCodecContent(AVCodecParameters *avCodecParameters, AVCodecContext **avCodecContext) {

    int ret;
    AVCodec *avCodec= avcodec_find_decoder(avCodecParameters->codec_id);

    if (!avCodec){
        isExit= true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }

    *avCodecContext= avcodec_alloc_context3(avCodec);
    if (!*avCodecContext){
        isExit = true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }

    ret = avcodec_parameters_to_context(*avCodecContext,avCodecParameters);
    if (ret<0){
        isExit= true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }
    //
    (*avCodecContext)->thread_type=FF_THREAD_FRAME;
    (*avCodecContext)->thread_count = 8;
    ret = avcodec_open2(*avCodecContext,avCodec,0);
    if (ret != 0){
        isExit = true;
        pthread_mutex_unlock(&init_mutex);
        return -1;
    }
    mimType =  av_bitstream_filter_init("h264_mp4toannexb");

    return 0;

}
void FAvFrame::decodeAVPackate() {
    pthread_mutex_lock(&init_mutex);
    avcodec_register_all();


    avFormatContext = avformat_alloc_context();
    avFormatContext->interrupt_callback.callback = avformat_ff_callback;
    avFormatContext->interrupt_callback.opaque = this;
    int ret;
    ret = avformat_open_input(&avFormatContext,url,0,0);
    if (ret != 0){
        LOGE("打开视频输入流失败");
        isExit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    ret = avformat_find_stream_info(avFormatContext,0);

    if (ret != 0){
        LOGE("查找视频流失败");
        isExit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    ret=-1;

    for (int i = 0; i < avFormatContext->nb_streams; ++i){
        if (avFormatContext->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){
            ret=0;
            avStreamIndex=i;
            time_base = avFormatContext->streams[i]->time_base;
            duration = avFormatContext->duration;
            break;
        }
    }

    if (ret!=0){
        isExit= true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }


    if (getAvCodecContent(avFormatContext->streams[avStreamIndex]->codecpar,&avCodecContext)!= 0){
        LOGE("获取解码器信息失败");
        isExit = true;
        pthread_mutex_unlock(&init_mutex);
        return;
    }

    avCodecContext->skip_loop_filter=AVDISCARD_NONKEY;

    LOGE("%s,%d,%d,%d,%d,%d,%d",avCodecContext->codec->name,
         avCodecContext->width,
         avCodecContext->height,
         avCodecContext->extradata_size,
         avCodecContext->extradata_size,
         avCodecContext->extradata,
         avCodecContext->extradata);

    pthread_mutex_unlock(&init_mutex);



}



void FAvFrame::starDecode() {
    LOGE("开始解码抽帧");
    int ret;
    int count = 0;
    while (ffStatus&&ffStatus->exit){
        if (isPause){
            av_usleep(1000*10);
            continue;
        }
        auto *avPacket=av_packet_alloc();
        ret= av_read_frame(avFormatContext,avPacket);
        if (ret != 0){
            //LOGE("获取视频avPacket 失败");
            av_usleep(1000*100);
            continue;
        }

        if (avPacket->stream_index==avStreamIndex){
            getAvPacketRefType2(avPacket);
        }
        count++;
    }

    isExit= true;
}
void FAvFrame::seekTo(int64_t sec, bool isCurrentGop) {
    if(duration <= 0){
        return;
    }


    if (isCurrentGop){
        int64_t res = sec/1000.0 * AV_TIME_BASE;
        seekTime = res;
        isPause = false;
    }else{
        if (sec>=0&&sec<duration){
            isPause= true;
            pthread_mutex_lock(&frame_mutex);
            queue->clearAvPacket();
            int64_t res = sec/1000.0 * AV_TIME_BASE;
            seekTime = res;
            avformat_seek_file(avFormatContext,-1,INT64_MIN,res,INT64_MAX,0);
            pthread_mutex_lock(&codecMutex);
            avcodec_flush_buffers(avCodecContext);
            pthread_mutex_unlock(&codecMutex);
            isPause = false;
            //decodeAvPacket(res);
            pthread_mutex_unlock(&frame_mutex);
        }
    }



}
void FAvFrame::init() {
    if (ffStatus != NULL && !ffStatus->exit){
        pthread_create(&decodeAvPacketThread,NULL,t_decode_avpacke,this);
    }


}
FAvFrame::FAvFrame(JavaCallHelper *_helper,const char *_url,FFStatus *ffStatus):helper(_helper),url(_url),ffStatus(ffStatus) {

    pthread_mutex_init(&init_mutex,NULL);
    pthread_mutex_init(&frame_mutex,NULL);
    pthread_mutex_init(&codecMutex,NULL);


}

FAvFrame::~FAvFrame() {
    pthread_mutex_destroy(&init_mutex);
    pthread_mutex_destroy(&frame_mutex);
    pthread_mutex_destroy(&codecMutex);
}
